package dao

import (
	"context"
	"gorm.io/gorm"
	"time"
)

type Tag struct {
	Id    int64  `gorm:"primaryKey,autoIncrement"`
	Name  string `gorm:"type:varchar(255)"`
	Uid   int64  `gorm:"index"`
	Ctime int64
	Utime int64
}

// TagBiz 某人给某个东西打标签
type TagBiz struct {
	Id    int64  `gorm:"primaryKey,autoIncrement"`
	BizId int64  `gorm:"index:biz_type_id"`
	Biz   string `gorm:"index:biz_type_id"`
	// 冗余字段，加快查询和删除
	Uid   int64 `gorm:"index"`
	Tid   int64
	Tag   *Tag  `gorm:"ForeignKey:Tid;AssociationForeignKey:Id;constraint:OnDelete:CASCADE"`
	Ctime int64 `bson:"ctime,omitempty"`
	Utime int64 `bson:"utime,omitempty"`
}

type TagDAO interface {
	CreateTag(ctx context.Context, tag Tag) (int64, error)
	CreateTagBiz(ctx context.Context, tagBiz []TagBiz) error
	GetTagByUid(ctx context.Context, uid int64) ([]Tag, error)
	GetTagByBiz(ctx context.Context, uid int64, biz string, bizId int64) ([]Tag, error)
	GetTags(ctx context.Context, offset, limit int) ([]Tag, error)
	GetTagsById(ctx context.Context, ids []int64) ([]Tag, error)
}

type tagDAO struct {
	db *gorm.DB
}

func NewTagDAO(db *gorm.DB) TagDAO {
	return &tagDAO{
		db: db,
	}
}

func (dao *tagDAO) CreateTag(ctx context.Context, tag Tag) (int64, error) {
	now := time.Now().UnixMilli()
	tag.Ctime = now
	tag.Utime = now
	err := dao.db.WithContext(ctx).Create(&tag).Error
	return tag.Id, err
}

// CreateTagBiz 这是一个 “覆盖” 的操作(先删除，再插入)
func (dao *tagDAO) CreateTagBiz(ctx context.Context, tagBiz []TagBiz) error {
	now := time.Now().UnixMilli()
	for _, t := range tagBiz {
		t.Ctime = now
		t.Utime = now
	}
	tb := tagBiz[0]
	return dao.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
		err := tx.Model(&TagBiz{}).Delete("uid = ? AND biz = ? AND biz_id = ?", tb.Uid, tb.Biz, tb.BizId).Error
		if err != nil {
			return err
		}
		return tx.Create(&tagBiz).Error
	})
}

func (dao *tagDAO) GetTagByUid(ctx context.Context, uid int64) ([]Tag, error) {
	var tags []Tag
	err := dao.db.WithContext(ctx).Where("uid = ?", uid).Find(&tags).Error
	return tags, err
}

// GetTagByBiz 查询某个用户对某个业务打的标签
func (dao *tagDAO) GetTagByBiz(ctx context.Context, uid int64, biz string, bizId int64) ([]Tag, error) {
	var tags []Tag
	err := dao.db.WithContext(ctx).
		Model(&Tag{}).                                                         // 查询的主表是 Tag
		InnerJoins("INNER JOIN tag_biz tb ON tb.tid = tag.id").                // 使用 InnerJoins，给 TagBiz 表设置别名 tb
		Where("tb.uid = ? AND tb.biz = ? AND tb.biz_id = ?", uid, biz, bizId). // 使用别名 tb 进行条件过滤
		Find(&tags).Error
	return tags, err
}

func (dao *tagDAO) GetTags(ctx context.Context, offset, limit int) ([]Tag, error) {
	var res []Tag
	err := dao.db.WithContext(ctx).Offset(offset).Limit(limit).Find(&res).Error
	return res, err
}

func (dao *tagDAO) GetTagsById(ctx context.Context, ids []int64) ([]Tag, error) {
	var res []Tag
	err := dao.db.WithContext(ctx).Where("id IN ?", ids).Find(&res).Error
	return res, err
}
